Bingo, Computer Graphics & Game Developer

Unidirectional Path Tracing

Deprecated(2017.03.30)

本文讲述的是在实现Atoms渲染器基于物理部分遇到问题的PBRT笔记解读

Atmos最先尝试实现的,是最最简单的路径追踪思想,也就是递归。搭配蒙特卡洛方法来计算渲染方程的无穷积分,本质上的思想是非常浅显易懂的。但这里我遇到了一个问题。

Lo(p,wo)=Le(wi,p,wo)+ΩBSDF(wi,p,wo)Li(wi,p)cosθdωL_o(p, \vec{w_o}) = L_e(\vec{w_i}, p, {w_o}) + \int_{\Omega}{BSDF(\vec{w_i}, p, \vec{w_o})L_i(\vec{w_i}, p)cos\theta d\vec{\omega}}

首先是将简单的递归改写成非递归的积分器Intergrator形式遇到了问题:单项路径追踪如何写成非递归样式?

其次是,为何Mitsubapbrt的Interator中Path Tracer实现中都有Direct illiumination过程,难道直接寻找光源进行求解计算不是Bias的?

尝试求解无果后,带着疑问又重新拾起当初一知半解看了大概的pbrt

P763P_{763}中的路径追踪最经典算法

11q1(P(p1ˉ+11q2(P(p2ˉ)+11q3(P(p3ˉ+...,)))\frac{1}{1-q_1}(P(\bar{p_1} + \frac{1}{1-q_2}(P(\bar{p_2})+\frac{1}{1-q_3}(P(\bar{p_3}+…,)))

PBRT中多次强调,递归计算出来的单项路径追踪最大的问题在于他的n个光线相交点的最后那一个,一定得是在光源上。否则,整条路径在最初点上的光照贡献就是00

这个概念非常易懂,但也正是这个特性,导致最最浅显概念上的非递归版本的单向路径追踪务必要额外增加一个栈来存放所有BSDF信息,这无疑只是简单的将递归转换一种写法,本质上并没有思想上的转换。

而真正核心的一句话,出现在P767P_{767}的伪代码中。

Possibly add emitted light at path vertex

明白直接光照计算方式的都知晓当前相交点使用Multiple importance sampling这一点,其最关键的就是主动向光源上计算贡献。

PBRT中分为两种采样方式,第一种适合光源较多的场景,是直接对场景中所有光源进行直接采样并累加最后求取期望,也就是E[f(x)+g(x)]E[f(x) + g(x)];蒙特卡洛表示这个求取方法在光源非常多的时候并不理想,这个期望也可以通过E[f(x)]+E[g(x)]E[f(x)]+E[g(x)]来完成,因此可以在场景中随机选取一个光源计算其贡献。

这里主动的求光源的光照贡献,其实和点光源/面积小的光源做法是一模一样的。

点光源 / 面积很小的光源会出现一个问题,就是单向路径追踪时,击中光源的概率实在太低了,在点光源上这个概率甚至是0!因此主动求取其光照贡献乘以所谓的权重依旧能保持无偏,但却解决了一个场景中只有点光源时无法照亮任何物体的问题。

P764P_{764}: In a similar manner, small area light sources can also be source of variance if not sampled explicityly.

改进后的单向路径追踪的算法改写为

Le(pipi1)f(pipi1pi2cosθi1)pA(pi)(j=1i2f(pi+1pjpj1)cosθj)pω(pj+1pj))\frac{L_e(p_i \to p_{i-1})f(p_i \to p_{i-1} \to p_{i-2}|cos\theta_{i-1}|)}{p_A(p_i)}(\prod^{i-2}_{j=1}\frac{f(p_{i+1} \to p_j \to p_{j-1})|cos\theta_j|)}{p_{\omega}(p_{j+1}-p_j)})

括号的左项就是代表了随机采样的光源的贡献(BSDF在光源-当前点上的f,乘以光源向相交点的LecosθL_e cos\theta),右方式子代表了BSDF采样的贡献累乘,由于每次循环所做的事情都一样。因此很自然的就可以写成非递归的形式。

可以看到这里直接寻找光源计算贡献的过程依然是Unbiased,只不过将最后击中光源的这一步操作分配到了路径的各个部分上而已。


假定最后一次光线在光源上相交点为pip_i,那么这一次的光源经常是会被忽略掉的。因为光源的贡献在上一个光路上已经计算过一次,也就是从表面AA上的pi1p_{i-1}的光源贡献是对整条路径而言的。

有两个例外,当光路从摄像机/Eye中发出直接击中光源时,此时光源的贡献是不被忽略的。还有一个就是PBRT中提到的Specular表面。因为在非指定光路的入射角度上,得到的BSDF采样结果都是0,只因在上一步的计算中,这一过程是被直接浪费掉的(光源贡献为0)。因此需要在找到新光路之前,补上这一部分的光照贡献(实现思路与点光源类似)。

最后上Path Tracing核心伪代码(来自PBRT)

Spectrum Li(param)

// Get information from first ray object intesection
<Declare common path integration variables>

    for(int bounces = 0;; ++bounces)
        // Direct illumination
        <Possibly add emitted light at path vertex>
        <Sample illumination from lights to find path contribution> 

        // BSDF Sampling
        <Sample BSDF to get new path direction> 

        // Russian rulette / Max depth
        <Possibly terminate the path>

        // Indirect illumination
        <Find next vertex of path>

return L